package org.danger.t7.common.meters;

import java.time.Duration;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;

import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Timer;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

/**
 * Meters
 */
@Component
public class Meters implements ApplicationContextAware {
    private static final Map<String, AtomicLong> gaugeMap = new HashMap<>();
    private static final Map<String, Counter> counterMap = new HashMap<>();
    private static Map<String, Timer> timers = new ConcurrentHashMap<>();

    private static MeterRegistry meter;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        meter = applicationContext.getBean(MeterRegistry.class);
    }


    public static AtomicLong gauge(KeyAndTags kt) {
        return gauge(kt, 0);
    }

    public static AtomicLong gauge(KeyAndTags kt, long defaultValue) {
        if (!gaugeMap.containsKey(kt.cacheKey())) {
            AtomicLong g=meter.gauge(kt.getKey(), kt.getTags(), new AtomicLong(defaultValue));
            gaugeMap.put(kt.cacheKey(), g);
        }
        return gaugeMap.get(kt.cacheKey());
    }

    public static Counter counter(KeyAndTags kt) {
        if (!counterMap.containsKey(kt.cacheKey())) {
            counterMap.put(kt.cacheKey(), meter.counter(kt.getKey(), kt.getTags()));
        }
        return counterMap.get(kt.cacheKey());
    }

    public static Timer timer(KeyAndTags kt) {
        if (!timers.containsKey(kt.cacheKey())) {
            Timer timer = Timer.builder(kt.getKey())
                    .tags(kt.getTags())
                    .publishPercentiles(0.5, 0.95) // median and 95th percentile (1)
                    .publishPercentileHistogram() // (2)
                    .serviceLevelObjectives(Duration.ofMillis(100)) // (3)
                    .minimumExpectedValue(Duration.ofMillis(1)) // (4)
                    .maximumExpectedValue(Duration.ofSeconds(10))
                    .register(meter);
            timers.put(kt.cacheKey(), timer);
        }
        return timers.get(kt.cacheKey());
    }

}
